// *************************************************************************************************
//
// MK3D Engine
// --------------------------------------
// Copyright (C) 2007-2011 Zeb (mailto: zebbey@gmail.com)
//
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//
// *************************************************************************************************

#ifndef __H_MK_ENTITY_MODEL__
#define __H_MK_ENTITY_MODEL__

#include "mk_entity.h"
#include "mk_model.h"
#include "mk_render_define.h"

namespace MK3DE
{
	class CScene;
	class CHardwareBuffer;
	class CRenderInput;
	class CMaterial;
	class CShader;

	class MK3DE_API CEntityModel : public CEntity
	{
	public:
		CEntityModel(CScene* pScene);
		~CEntityModel();

		//! Get entity render type and entity type.
		ENTITY_RENDER_TYPE GetRenderType() { return ERT_SOLID; }
		ENTITY_TYPE GetType() { return ET_MODEL; }

		//! Load the model from model file.
		/**
		\param modelFile : the file name of the model file.
		\return : If the method succeeds, return TRUE or return FALSE.
		*/
		BOOL LoadFromFile(const char* modelFile);

		//! Return the source model file name.
		std::string GetFileName() { return _fileName; }

		//! Set animation's interval time.
		/**
		\param time : the interval time(ms)
		\return : None
		*/
		void SetAnimationInterval(float time);

		//! Use a animation from specified index and started time.
		/**
		\param idx : the index of the animation.
		\param time : started time.
		\return : If the animation exist, then return TRUE or return FALSE.
		*/
		BOOL UseAnimation(DWORD idx, float time);

		//! Get the core model instance.
		CModel* GetCoreModel() { return _model; }

		//! Update the model entity.
		void Update(DWORD dt, BOOL bChanged);

		//! Render this model.
		void Render();

		//! Render shadow.
		void RenderShadow(const D3DXMATRIX& matViewProj, const D3DXMATRIX& matWrap);

		//! Collide with a ray.
		BOOL CollideWithRay(D3DXVECTOR3& pos, float x, float y, float z, D3DXVECTOR3& dir);

		//! Add a new material.
		void AddMaterial(CMaterial* material) { _vecMaterial.push_back(material); }

		//! Remove a material
		BOOL RemoveMaterial(DWORD idx);

		//! Get material count.
		DWORD GetMaterialCount() { return (DWORD)_vecMaterial.size(); }

		//! Get each material and the sub mesh count in every group by material index.
		BOOL GetMaterialInfo(DWORD idx, /* out */ CMaterial*& material, /* out */ int& groupSubMeshCount);

		//! Sort sub mesh by material index.
		void SortSubMeshByMaterialIdx();

		//! Update vertex buffer.
		BOOL UpdateVBFaceIndex();
		BOOL UpdateVBPosNormal(LOCK_OPTION lockOp);
		BOOL UpdateVBTexCoord();

	public:
		/** internal method */
		void _AttachSceneNode(CSceneNode* pNode);

	public:
		//! Set whether use software to do skinning.
		static void EnableSoftwareSkin(BOOL bEnable) { _s_softwareSkin = bEnable; }

	private:
		BOOL __LoadModel(const char* fileName);
		BOOL __LoadMaterial(const char* materialFile);
		void __UpdateLocalAABB();
		BOOL __RenderSkeleton();
		void __UpdateBonePalette(CShader* shader);
		BOOL __UpdateVertexBuffer();
		void __BindRenderInput();
		void __BindRenderInputShadow(const D3DXMATRIX& matViewProj, const D3DXMATRIX& matWrap);
		void __RenderSubMesh(int idx, std::string& baseTechName, int vertexCount, int& indexBufferOffset, int& currentSubMeshIdx);
		void __RenderSubMeshShadow(int idx, std::string& baseTechName, int vertexCount, int& indexBufferOffset, int& currentSubMeshIdx);

	protected:
		static BOOL _s_softwareSkin;

	private:
		CModel* _model;

		BOOL _hasSkin;

		CHardwareBuffer* _vbPosition;
		CHardwareBuffer* _vbNormal;
		CHardwareBuffer* _vbUV;
		CHardwareBuffer* _vbBoneIndex;
		CHardwareBuffer* _vbWeight;

		CHardwareBuffer* _ibIndex;

		CRenderInput* _renderInput;
		CRenderInput* _renderInputShadow;

		std::vector<CMaterial*> _vecMaterial;

		//! Sub mesh count in every material group(sorted).
		std::vector<int>	_vecGroupSubMeshCount;
		CShader* _shader;

		std::string _fileName;
	};
};

#endif // __H_MK_ENTITY_MODEL__